<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>面试</title>
    <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
    <script src="./vue.min.js"></script>
    <style>
        .flex{
            display: flex;
            position: relative;
        }
        .left{
            width: 240px;
            position: absolute;
            left: 0;
            top: 0;
            height: auto;
        }
        .left p{
            font-size: 14px;
            padding: 0;
            margin: 0;
        }
        .left .tip{
            font-size: 20;
            font-weight: bold;
        }
        .right{
            flex: 1;
            padding-left: 250px;
            height: 100vh;
            overflow: scroll;
        }
        .right p{
            padding: 0;
            margin: 0;
            text-indent: 4em;
            line-height: 30px;
        }
        .right .indent6{
            text-indent: 6em;
        }
        .right .title{
            font-weight: bold;
        }
        .title{
            color: #a0cfff;
        }
        h3{
            background-color: #ccc;
        }
        h4{
            text-indent: 2em;
            padding: 0;
            margin: 0;
            color: #409eff;
        }
        ul,li{
            list-style: none;
        }
        a{
            text-decoration: none;
        }
    </style>
</head>
<body>
    <div id="app" class="flex">
        <div class="left">
            <ul>
                <li v-for="menu in menus">
                    <p class="tip">{{menu.name}}</p>
                    <template v-if="menu.children">
                        <p style="padding-left:20px" v-for="title in menu.children" :key="title.id">
                            <a :href=`#${title.id}` style="color:#000">{{title.name}}</a>
                        </p>
                    </template>
                </li>
            </ul>
        </div>
        <div class="right">
            <h3>CSS</h3>
            <div>
                <div id="css1">
                    <h4>样式穿透</h4>
                    <p>1、/deep/</p>
                    <p>2、>>></p>
                </div>
                <div id="css2">
                    <h4>BFC</h4>
                    <p class="title">定义</p>
                    <p class="indent6">BFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。</p>
                    <p class="title">如何创建BFC</p>
                    <p class="indent6">浮动元素</p>
                    <p class="indent6">绝对定位元素</p>
                    <p class="indent6">display:inline-block，display:table-cell，display:flex，display:inline-flex</p>
                    <p class="indent6">overflow指定除了visible的值</p>
                    <p class="title">特点</p>
                    <p class="indent6">在BFC中，块级元素从顶端开始垂直地一个接一个的排列</p>
                    <p class="indent6">如果两个块级元素属于同一个BFC，它们的上下margin会重叠（或者说折叠），以较大的为准。但是如果两个块级元素分别在不同的BFC中，它们的上下边距就不会重叠了，而是两者之和。
                    </p>
                    <p class="indent6">BFC的区域不会与浮动的元素区域重叠，也就是说不会与浮动盒子产生交集，而是紧贴浮动边缘。</p>
                    <p class="indent6">计算BFC的高度时，浮动元素也参与计算。BFC可以包含浮动元素。（利用这个特性可以清除浮动）</p>
                    <p class="indent6">BFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素。</p>
                    <p class="title">作用</p>
                    <p class="indent6">1、解决外边距折叠问题</p>
                    <p class="indent6">2、制作两栏布局：BFC的区域不会与浮动的元素区域重叠。</p>
                    <p class="indent6">3、清除元素内部的浮动</p>

                </div>
                <div id="css3">
                    <h4>flex布局</h4>
                    <p>flex-direction 属性设置容器主轴的方向：row | row-reverse | column | column-reverse;</p>
                    <p>flex-wrap属性用于设置当项目在容器中一行无法显示的时候如何处理：nowrap | wrap | wrap-reverse（向上换行，第一行位于下方）;</p>
                    <p>justify-content属性用于设置项目在容器中的对齐方式；flex-start | flex-end | center |space-between | space-around；</p>
                    <p>align-items定义了项目在交叉轴上是如何对齐显示的；flex-start | flex-end | center | baseline(项目的第一行文字的基线对齐) | stretch(如果项目未设置高度或者高度为auto，将占满整个容器的高度)</p>
                    <p>align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线，该属性不起作用:flex-start | flex-end | center | space-between | space-around | stretch;</p>
                    <p>flex-group属性用来控制当前项目是否放大显示。默认值为0，表示即使容器有剩余空间也不放大显示。如果设置为1，则平均分摊后放大显示。</p>
                    <p>flex-shrink属性表示元素的缩小比例。默认值为1，如果空间不够用时所有的项目同比缩小。如果一个项目的该属性设置为0，则空间不足时该项目也不缩小。</p>
                    <p>flex-basis属性表示表示项目占据主轴空间的值。默认为auto，表示项目当前默认的大小。如果设置为一个固定的值，则该项目在容器中占据固定的大小。</p>
                    <p>flex属性是 flex-grow属性、flex-shrink属性、flex-basis属性的简写。默认值为：0 1 auto；</p>
                    <p>align-self属性表示当前项目可以和其他项目拥有不一样的对齐方式。它有六个可能的值。默认值为auto;flex-start | flex-end | center | baseline | stretch </p>
                    <p>order属性定义项目的排列顺序。数值越小，排列越靠前，默认为0。</p>
                </div>
            </div>
            <h3>VUE</h3>
            <div>
                <div id="vue1">
                    <h4>指令</h4>
                    <p>v-if、v-model、v-show、v-for、v-once、v-on、v-bind；</p>
                    <p>在 vue3 中，当 v-if 与 v-for 一起使用时，v-if 具有比 v-for 更高的优先级。vue2中，v-for优先级大于v-ifs</p>
                </div>
                <div id="vue2">
                    <h4>生命周期</h4>
                    <div>
                        <p class="title">钩子</p>
                        <div>
                            <p class="indent6">create阶段：vue实例被创建 </p>
                            <p class="indent6">beforeCreate: 创建前，此时data和methods中的数据都还没有初始化 </p>
                            <p class="indent6">created： 创建完毕，data中有值，未挂载</p>
                            <p class="indent6">mount阶段： vue实例被挂载到真实DOM节点 </p>
                            <p class="indent6">beforeMount：可以发起服务端请求，去数据 </p>
                            <p class="indent6">mounted: 此时可以操作Dom</p>
                            <p class="indent6">update阶段：当vue实例里面的data数据变化时，触发组件的重新渲染</p>
                            <p class="indent6">beforeUpdate</p>
                            <p class="indent6">updated</p>
                            <p class="indent6">destroy阶段：vue实例被销毁 </p>
                            <p class="indent6">beforeDestroy：实例被销毁前，此时可以手动销毁一些方法 </p>
                            <p class="indent6">destroyed</p>
                        </div>
                        <p class="title">父子组件生命周期执行顺序</p>
                        <div>
                            <p class="indent6">父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted</p>
                        </div>
                        <p class="title">父组件监听子组件的生命周期方法</p>
                        <div>
                            <p class="indent6">1、$emit：自组件传值给父组件</p>
                            <p class="indent6">2、@hook 使用方法：< child-component @hook:mounted="handleDoSomething">< /child-component ></p>
                        </div>
                    </div>
                </div>
                <div id="vue3">
                    <h4>VUEX:状态管理模式</h4>
                    <div>
                        <p class="title">定义</p>
                        <div>
                            <p class="indent6">Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store（仓库）。 </p>
                            <p class="indent6">（1）Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候，若 store 中的状态发生变化，那么相应的组件也会相应地得到高效更新。</p>
                            <p class="indent6">（2）改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。</p>
                        </div>
                        <p class="title">属性</p>
                        <p class="indent6">1、State：定义了应用状态的数据结构，可以在这里设置默认的初始状态。</p>
                        <p class="indent6">2、Getter：允许组件从 Store 中获取数据，mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。</p>
                        <p class="indent6">3、Mutation：是唯一更改 store 中状态的方法，且必须是同步函数。</p>
                        <p class="indent6">4、Action：用于提交 mutation，而不是直接变更状态，可以包含任意异步操作。</p>
                        <p class="indent6">5、Modules：允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。</p>
                        <p class="title">vuex持久化</p>
                        <p class="indent6">1、localStorage   2、vuex插件：vue-persist</p>
                    </div>
                </div>
                <div id="vue4">
                    <h4>路由模式</h4>
                    <div>
                        <p class="title">hash模式和history模式区别</p>
                        <p class="indent6">1、url 展示上，hash 模式有“#”，history 模式没有</p>
                        <p class="indent6">2、刷新页面时，hash 模式可以正常加载到 hash 值对应的页面，而 history 没有处理的话，会返回 404，一般需要后端将所有页面都配置重定向到首页路由；打包后使用hash，使用history会出现空白页</p>
                        <p class="indent6">3、hash 可以支持低版本浏览器和 IE。</p>
                        <p class="title">hash模式和history模式是如何实现的</p>
                        <p class="indent6">1、hash模式：#后面 hash 值的变化，不会导致浏览器向服务器发出请求，浏览器不发出请求，就不会刷新页面。同时通过监听 hashchange 事件可以知道 hash 发生了哪些变化，然后根据 hash 变化来实现更新页面部分内容的操作。</p>
                        <p class="indent6">2、history模式：主要是 HTML5 标准发布的两个 API，pushState 和 replaceState，这两个 API 可以在改变 url，但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作。</p>
                    </div>
                    <h4>路由导航守卫</h4>
                    <div>
                        <p>全局：router.beforeEach、router.afterEach</p>
                        <p>路由独享：（写在单个路由配置中）beforeEach</p>
                        <p>组件内：beforeRouteEnter（进入守卫）、beforeRouteLeave（离开守卫）</p>
                        <p class="title">完整的导航解析：</p>
                        <p class="indent6">1、导航被触发</p>
                        <p class="indent6">2、在失活的组件里调用离开守卫。</p>
                        <p class="indent6">3、调用全局的 beforeEach 守卫。</p>
                        <p class="indent6">4、在重用的组件里调用 beforeRouteUpdate 守卫 </p>
                        <p class="indent6">5、在路由配置里调用 beforeEnter。</p>
                        <p class="indent6">6、解析异步路由组件。</p>
                        <p class="indent6">7、在被激活的组件里调用 beforeRouteEnter。</p>
                        <p class="indent6">8、调用全局的 beforeResolve 守卫 (2.5+)。</p>
                        <p class="indent6">9、导航被确认。</p>
                        <p class="indent6">10、调用全局的 afterEach 钩子。</p>
                        <p class="indent6">11、触发 DOM 更新。</p>
                        <p class="indent6">12、用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。</p>
                    </div>
                </div>
                <div id="vue5">
                    <h4>双向绑定原理</h4>
                    <div>
                        <p>是通过数据劫持结合发布订阅模式的方式来实现的，通过Object.defineProperty()来劫持各个属性的setter，getter，在数据变动时发布消息给订阅者，触发相应的监听回调来渲染视图。也就是说数据和视图同步，数据发生变化，视图跟着变化，视图变化，数据也随之发生改变。</p>
                        <p>当一个Vue实例创建时，Vue会遍历data选项的属性，通过Object.defineProperty()来劫持各个属性的setter，getter并且在内部追踪相关依赖，在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例，它会在组件渲染的过程中把属性记录为依赖，之后当依赖项的 setter 被调用时，会通知 watcher重新计算，从而致使它关联的组件得以更新。</p>
                    </div>
                </div>
                <div id="vue6">
                    <h4>组件传值</h4>
                    <div>
                        <p class="indent6">props 和 $emit、v-model、 .sync、 ref、$bus、$attrs、$listeners、$parent、$children、$root、provide、inject、vuex 、路由传值、插槽
                        Vue提倡单向数据流 ，自组件不能修改props里的值</p>
                        <p class="indent6">ref除了可以获取本页面的dom元素，还可以拿到子组件中的data和去调用子组件中的方法</p>
                        <p class="indent6">使用Vue.set(param,'key',value)给组件动态添加未知的参数属性；第一个参数是对象或数组，如果是数组，第二个参数就是数组中的下标，如果是对象，第二个参数就是属性名，第三个参数是修改的值</p>
                    </div>
                </div>
                <div id="vue7">
                    <h4>props和data优先级</h4>
                    <div>
                        <p class="indent6">props > methods > data > computed > watch</p>
                    </div>
                </div>
                <div id="vue8">
                    <h4>组件中 data 为什么是一个函数？</h4>
                    <div>
                        <p class="indent6">因为组件是用来复用的，且 JS 里对象是引用关系，如果组件中 data 是一个对象，那么这样作用域没有隔离，子组件中的 data 属性值会相互影响，如果组件中 data 选项是一个函数，那么每个实例可以维护一份被返回对象的独立的拷贝，组件实例之间的 data 属性值不会互相影响；而 new Vue 的实例，是不会被复用的，因此不存在引用对象的问题</p>
                    </div>
                </div>
                <div id="vue9">
                    <h4>key的作用</h4>
                    <p class="title">定义</p>
                    <p class="indent6">key是虚拟DOM对象的标识，当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】
                        随后vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较</p>
                    <p class="title">比较规则如下:</p>
                    <p class="indent6">1.旧虚拟DOM中找到了新虚拟DOM相同的Key：
                        若虚拟DOM中内容没变，直接使用之前的真实DOM。
                        若虚拟DOM中内容变了，则生成新的真实DOM，随后替换掉页面的真实DOM</p>
                    <p class="indent6">2.旧虚拟DOM中未找到与新虚拟DOM相同的Key;创建新的真实DOM，随后渲染到页面</p>
                    <p class="title">用index作为key可能会引发的问题:</p>
                    <p class="indent6">1.若对数据进行：逆序添加，逆序删除等破坏顺序操作，会产生没有必要的真实DOM更新，页面效果没问题，但是效率低</p>
                    <p class="indent6">2.如果结构中还包含输入类的DOM，会产生错误DOM更新，界面会出现问题</p>
                </div>
                <div id="vue10">
                    <h4>vue中用diff就是通过同级比较dom树，来进行重绘或者回流。分为三种情况：</h4>
                    <p>1.根元素变了，删除重建整个DOM树</p>
                    <p>2.根元素没变，属性改变，更新属性，元素复用</p>
                    <p>3.根元素和子元素都没变，但元素里的内容改变</p>
                    <p>没有设置key值（key就是for循环里面的key）;v-for 不会移动DOM，而是复用DOM元素，就地更新</p>
                </div>
                <div id="vue11">
                    <h4>计算属性与监听属性</h4>
                    <p class="title">计算属性：(computed)</p>
                    <p class="indent6">-get、set属性，底层借助了object.defineproperty方法提供的getter和setter。
                        -当读取计算属性里的值时，get会被调用，初次读取值或者所依赖的数据发生改变时都会被调用。
                        -有缓存，只有所依赖的数据发生改变时缓存的值才会更新。
                        -当计算属性的值被直接修改时会执行set
                    </p>
                    <p class="title">监听属性:(watch)</p>
                    <p class="indent6">
                        -当监视属性变化时，回调函数自动调用，进行相关操作。
                        -监听属性有两种写法:new Vue里传入watch配置或者通过vm.$watch()监听。
                        -有handler函数，其身上有旧值与新值(newValue,oldValue)
                        -没有缓存
                        -检测data里的属性，也可以监听计算属性里的值
                        -默认不检测对象内部值的改变，可配置deep:true开启深度监听
                    </p>
                </div>
                <div id="vue12">
                    <h4>组件的创建过程</h4>
                    <p>组件的本质是一个VueComponent的构造函数，是Vue.extend生成的。每次调用Vue.extend返回的都是一个新的VueComponent。
                        -非单文件组件-使用Vue.extend({})创建组件-在vue实例对象中注册组件components:{},(全局组件Vue.component()和局部组件)-使用组件
                        -单文件组件-创建组件文件.vue-使用import引入组件-在components里注册组件-使用标签形式使用组件</p>
                </div>
                <div id="vue13">
                    <h4>nextTick</h4>
                    <p>nextTick所指定的回调会在浏览器更新DOM完毕之后再执行。</p>
                    <p>原理：vue更新DOM是异步更新；nextTick内部就是调用宏任务和微任务来完成事件调用的机制，让nextTick里的回调在一个事件循环的最后执行。</p>
                    <p>-作用：在下一次dom更新结束之后执行指定回调
                        -什么时候调用：当改变数据后，要基于更新后的新dom进行某些操作时</p>
                </div>
                <div id="vue14">
                    <h4>Vue数据更新，页面却没有更新</h4>
                    <p>1、Vue无法检测实例被创建时不存在于data中的变量</p>
                    <p>2、vue也不能检测到data中对象的动态添加和删除：Vue.set()、Object.assign()、Vue.delete()</p>
                    <p>3、变量为数组时：Vue.set()</p>
                    <p>4、异步获取接口数据，DOM数据不发现变化：Vue.nextTick()</p>
                    <p>5、循环嵌套层级太深，视图不更新:this.$forceUpdate()</p>
                    <p>6、路由参数变化时，页面不更新(数据不更新)：1.通过 watch 监听 $route 的变化。2.给<router-view> 绑定key属性</p>
                    <p>7、变量通过赋值来定义的：<br/>
                        在 Vue 中有两种类型的变量：响应式变量和非响应式变量。从后端获取的变量通常是响应式变量;而通过赋值来定义的变量通常是非响应式变量，如果你修改了这些非响应式变量，Vue 并不会监测到它们的变化，所以页面不会改变。可以使用 Vue.set 方法或者数组的变异方法（例如 push、splice 等）</p>
                </div>
                <div id="vue15">
                    <h4>vue2和vue3响应式原理</h4>
                    <p>vue2：在vue2中利用的是原生js下边的Object.defineProperty()进行数据劫持，在通过里面的getter和setter方法，进行查看和数据的修改，通过发布、订阅者模式进行数据与视图的响应式。</p>
                    <p>vue3:对于基本数据类型来说，响应式依然是靠Object.defineProperty()的get和set来完成的;<br/>对于对象类型的数据：通过Proxy代理：拦截对象中任意属性的变化，包括属性值得读写、添加、删除等操作等,通过Reflect反射函数进行操作</p>
                    <p>vue2双向绑定的缺点:不能监听对象的新增属性和删除属性(使用vue.$set设置);无法正确的监听数组的方法（如果是通过index下标进行修改，不会被vue监听到，要是要修改可以通过数组的常用方法进行修改）</p>
                </div>
            </div>
            <h3>ES6</h3>
            <div>
                <div id="es1">
                    <h4>普通函数和箭头函数区别</h4>
                    <div>
                        <p>1、箭头函数全都是匿名函数：普通函数可以有匿名函数，也可以有具名函数。</p>
                        <p>2、箭头函数不能用于构造函数：普通函数可以用于构造函数，以此创建对象实例。</p>
                        <p>3、箭头函数中 this 的指向不同：在普通函数中，this 总是指向调用它的对象，如果用作构造函数，它指向创建的对象实例。箭头函数this：指向最近的上级this，箭头函数中的this是定义函数的时候绑定，而不是在执行函数的时候绑定。任何方法都改变不了其指向，如 call() , bind() , apply()</p>
                        <p>4、箭头函数不具有 arguments 对象：每一个普通函数调用后都具有一个
                            arguments 对象，用来存储实际传递的参数。但是箭头函数并没有此对象。</p>
                        <p>5、箭头函数不具有 prototype 原型对象。箭头函数不具有 super。
                            箭头函数不具有 new.target</p>
                    </div>
                </div>
                <div id="es2">
                    <h4>数组方法</h4>
                    <div>
                        <p>1、push：向数组的末尾添加新内容，改变原数组.</p>
                        <p>2、pop：删除数组的最后一项，改变原数组.</p>
                        <p>3、shift：删除数组的第一项，改变原数组</p>
                        <p>4、unshift：向数组首位添加新内容，改变原数组</p>
                        <p>
                            5、slice()：按照条件查找出其中的部分内容，不改变原数组
                            <p class="indent6">1、array.slice(n, m)，从索引n开始查找到m处（不包含m）</p>
                            <p class="indent6">2、array.slice(n) 第二个参数省略，则一直查找到末尾</p>
                            <p class="indent6">3、array.slice(0)原样输出内容，可以实现数组克隆</p>
                            <p class="indent6">4、array.slice(-n,-m) slice支持负参数，从最后一项开始算起，-1为最后一项，-2为倒数第二项</p>
                        </p>
                        <p>
                            6、splice()：对数组进行增删改；原有数组改变
                            <p class="indent6">增加：ary.splice(n,0,m)从索引n开始删除0项，把m或者更多的内容插入到索引n的前面</p>
                            <p class="indent6">修改：ary.splice(n,x,m)从索引n开始删除x个，m替换删除的部分</p>
                            <p class="indent6">删除：ary.splice(n,m) 从索引n开始删除m个内容</p>
                        </p>
                        <p>7、join()：用指定的分隔符将数组每一项拼接为字符串，不改变原数组</p>
                        <p>8、concat()：用于连接两个或多个数组，不改变原数组</p>
                        <p>9、indexOf()：检测当前值在数组中第一次出现的位置索引，不改变原数组</p>
                        <p>10、lastIndexOf()：检测当前值在数组中最后一次出现的位置索引，不改变原数组</p>
                        <p>11、includes()：判断一个数组是否包含一个指定的值，不改变原数组</p>
                        <p>12、sort()：排序，改变原数组</p>
                        <p>13、reverse()：把数组倒过来排列，改变原数组</p>
                        <p>14、forEach()：循环，不改变原数组</p>
                        <p>15、map：循环，该方法有返回值,返回一个新数组,新数组的长度和原数组长度相等，不改变原数组</p>
                        <p>16、filter：过滤，有返回值, 过滤出符合条件的元素，不改变原数组</p>
                        <p>17、some：判断数组中有没有符合条件的项(只要有,就返回true),如果一个都没有,才返回false</p>
                        <p>18、every：判断数组中所有的项是否满足要求,如果全都满足,才返回true,否则返回false</p>
                        <p>19、find：找到符合条件的项,并且返回第一项</p>
                        <p>20、findindex：找到符合条件的项的下标,并且返回第一个</p>
                        <p>16、filter：过滤，有返回值, 过滤出符合条件的元素，不改变原数组</p>
                    </div>
                    <h4>数组循环</h4>
                    <div>
                        <p>filter返回新数组，不改变原来数组，返回整体，过滤出满足条件的部分数据，不会改变原数组，会生成新的数组</p>
                        <p>find返回具体对象，返回第一个匹配的对象</p>
                        <p>findIndex返回第一个符合条件的索引号</p>
                        <p>map：全部循环，不改变原数组，会生成新的数组；返回处理后的值和新数组length不一定相等</p>
                        <p>forEach：全部循环，可以重新给原数组赋值</p>
                        <p>some：若目标数组中某一个对象都符合条件则返回true，否则返回false</p>
                        <p>every：若目标数组中每一个对象都符合条件则返回true，否则返回false</p>
                    </div>
                    <h4>复制数组</h4>
                    <div>
                        <p>1、浅复制</p>
                        <p class="indent6">concat():方法不会更改现有数组，而是返回一个新数组，其中包含已连接数组的值。</p>
                        <p class="indent6">slice():方法以新的数组对象，返回数组中被选中的元素;不会改变原始数组;</p>
                        <p class="indent6">扩展运算符:不会改变原始数组;var [ ...newArr ] = arr;</p>
                        <p class="indent6">Object.assign();不会改变原始数组;</p>
                        <p>2、深复制</p>
                        <p class="indent6">JSON.parse(JSON.stringify(arr))；但是该方法是有局限性的，会忽略 undefined、symbol类型；不能序列化函数；不能解决循环引用的对象</p>
                        <p class="indent6">递归</p>
                        <p class="indent6">利用lodash的深拷贝函数；_.cloneDeep(value)</p>
                    </div>
                </div>
                <div id="es3">
                    <h4>在Axios中取消请求最核心的方法是CanelToken；</h4>
                    <div>
                        <p>const CancelToken = axios.CancelToken;</p>
                        <p>const source = CancelToken.source();</p>
                    </div>
                </div>
                <div id="es4">
                    <h4>new发生了什么</h4>
                    <div>
                        <p>1、new首先是创建了一个空对象；let obj = new Object();</p>
                        <p>2、我们将这个空对象的__proto__成员指向了原函数对象prototype成员对象；obj.proto = person.prototype；</p>
                        <p>3.通过使用call方法执行构造函数并把this绑定到实例对象身上。person.call(obj)</p>
                        <p>4.如果函数没有返回其他对象，那么 new 构造函数会自动返回这个新对象</p>
                    </div>
                </div>
                <div id="es5">
                    <h4>set和map的区别</h4>
                    <p>1.Map是键值对，Set是值的集合，当然键和值可以是任何的值；</p>
                    <p>2.Map可以通过get方法获取值，而set不能因为它只有值；</p>
                    <p>3.都能通过迭代器进行for...of遍历；</p>
                    <p>4.Set的值是唯一的可以做数组去重，Map由于没有格式限制，可以做数据存储</p>
                    <p>5.map和set都是stl中的关联容器，map以键值对的形式存储，key=value组成pair，是一组映射关系。set只有值，可以认为只有一个数据，并且set中元素不可以重复且自动排序。</p>
                    <p>6.Set数据结构的声明方式分为两种：1、直接实例化一个空的Set()构造函数；2、在Set构造函数中传入一个数组。Set构造函数可以用来做数组去重等操作。</p>
                    <p>7.Map数据结构的声明方式分为两种：1、直接实例化一个空的Map（）构造函数；2、在Map构造函数中传入一个带有键值对的数组。PS:创建Map集合有两种：1.直接new Map()创建实例对象；2.接收一个数组作为参数来创建实例对象（该数组的成员必须是一个个表示键值对的数组）。</p>
                    <p class="indent6">Map集合常用属性和方法：
                        1、.has(key);查找key,返回布尔值。
                        2、.get(key) 根据key查找value。
                        3、.set(key,value) 添加键值对。
                        4、.size获取存储的数量。
                    </p>
                </div>
                <div id="es6">
                    <h4>promise</h4>
                    <p class="title">定义</p>
                    <p class="indent6">它是新增的构造器（Array, Object, Promise），用来优化实现异步操作。在没有它之前，javascript中的异步处理，大多是利用回调函数来实现的。是异步操作的一种解决方案，一般用来解决层层嵌套的回调函数（回调地狱 callback hell）的问题。</p>
                    <p class="indent6">1、只能通过构造函数实例化创建。</p>
                    <p class="indent6">2、状态：pending(进行中)、resolved(fulfilled)（已成功）、rejected（已失败）;状态转换是不可逆的。</p>
                    <p class="indent6">3、Promise.race 类方法，多个 Promise 任务同时执行，返回最先执行结束的 Promise 任务的结果，不管这个 Promise 结果是成功还是失败。</p>
                    <p class="indent6">Promise.all 类方法，多个 Promise 任务同时执行。如果全部成功执行，则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected，则只返回 rejected 任务的结果。</p>

                </div>
                <div id="es7">
                    <h4>ES6新特性</h4>
                    <p>1.新增了块级作用域（let，const）</p>                   
                    <p>2.提供了定义类的语法糖（class）</p>                        
                    <p>3.新增了一种基本数据类型（Symbol）</p>
                    <p>4.新增了变量的解构赋值 -- 从数组或者对象中提取值，按照对应的位置，对变量赋值（在数组解构中，只 要解构的目标可以遍历，就可以实现解构赋值）。</p>
                    <p class="indent6">解构赋值的特性：</p>
                    <p class="indent6">4.1：声明的变量数量与数组的元素数量一致：</p>
                    <p class="indent6">4.2：声明的变量的数量大于数组的元素的数量：</p>
                    <p class="indent6">4.3：声明的变量的数量小于数组的元素的数量：</p>
                    <p class="indent6">4.4：使用剩余运算符进行解构赋值：</p>
                    <p class="indent6">4.5：可遍历对象 例如：字符串：</p>
                    <p>5.函数参数允许设置默认值，引入了rest参数，新增了箭头函数。</p>
                    <p>6.数组新增了一些API，如isArray / from / of 方法；数组实例新增了 entries()，keys() 和 values() 等方法。</p>
                    <p>7.对象和数组新增了扩展运算符</p>
                    <p>8.ES6新增了模块化（import / export）</p>
                    <p>9.ES6新增了Set和Map数据结构。</p>
                    <p>10.ES6原生提供Proxy构造函数，用来生成Proxy实例</p>
                    <p>11.ES6新增了生成器（Generator）和遍历器（Iterator）</p>
                </div>
            </div>
            <h3>JAVASCRIPT</h3>
            <div>
                <div id="js1">
                    <h4>MVVM框架:让数据自动地双向同步 不再需要操作DOM</h4>
                    <p>view：视图</p>
                    <p>model：模型；数据</p>
                    <p>viewmodel：视图数据模型</p>
                </div>
                <div id="js2">
                    <h4>数字精度丢失</h4>
                    <p>用插件decimal 或者 bignumber </p>
                </div>
                <div id="js3">
                    <h4>柯里化</h4>
                    <p>又称部分求值（Partial Evaluation），是把接受多个参数的函数变换成接受一个单一参数（最初函数的第一个参数）的函数，并且返回接受余下的参数而且返回结果的新函数的技术 </p>
                    <p>好处：参数复用；提前返回；延迟计算/运行；</p>
                </div>
                <div id="js4">
                    <h4>复杂类型和基本类型区别</h4>
                    <p>基本类型：number、string、null、undefined、boolean；</p>
                    <p>复杂类型：object、array、function；</p>
                    <p>首先是复杂数据类型保存在堆内存中，而基本数据类型保存在栈内存中，然后声明一个复杂数据类型变量中保存的是一个复杂数据类型的地址，基本数据类型保存的是一个具体的值</p>
                    <p>声明两个复杂数据类型指向同一个地址的时候，改变一个另一个也会跟着改变</p>
                </div>
                <div id="js5">
                    <h4>JavaScript原型，原型链</h4>
                    <p>每个对象都会在其内部初始化一个属性，就是prototype（原型）
                        当我们去访问一个对象的属性时，如果这个对象内部不存在这个属性，那么他就会去prototype里面找这个属性，这个属性prototype就会有自己的prototype。
                        就这样一层一层的寻找，也就是我们平时所说的原型链的概念，顶端是null</p>
                </div>
                <div id="js6">
                    <h4>js中的显式原型和隐式原型</h4>
                    <p>隐式原型：__ proto__；对象具有属性（__ proto__）称为隐式原型；</p>
                    <p>显式原型：prototype；对象的隐式原型指向构造该对象的构造函数的显式原型 （prototype）。</p>
                    <p class="title">两者之间关系：</p>
                    <p class="indent6">隐式原型指向创建这个对象的构造函数的prototype</p>
                    <p class="title">二者的区别：</p>
                    <p class="indent6">prototype:专属于函数的一个属性，类型为对象，叫原型对象；作用：为了给将来自身所在的构造函数被new出来的实例做父级使用的</p>
                    <p class="indent6"> __ proto__：专属于对象数据的一个属性，类型为对象，叫隐士原型，作用：找父级</p>
                </div>
                <div id="js7">
                    <h4>深拷贝和浅拷贝</h4>
                    <p>//基本数据类型存储在栈内存中，引用数据类型存储在堆内存中。</p>
                    <p>浅拷贝和深拷贝都是对于引用数据类型来说的。</p>
                    <p>浅拷贝只是复制某个对象的指针（地址），导致它们都指向了堆内存中同一个数据，互相影响。-------经过赋值操作，两个对象都指向了堆内存中的同一个数据，所以其中一个发生变化时，另一个也会随着变化。（ = 就是浅拷贝）</p>
                    <p>深拷贝是在堆内存中创建一个一模一样的数据，然后把新数据的内存地址赋给新变量，这样旧变量和新变量就指向了不同的数据，也就不会互相影响。
                        在进行深拷贝操作时，会在堆内存中创建一个一模一样的数据，就指向了不同的数据，也就不会互相影响</p>
                    <p class="title">深拷贝方法</p>
                    <p class="indent6">1、JSON.stringify()结合JSON.parse()；可以实现数组和对象的深拷贝，不能处理函数：原因是：undefined、symbol及任意函数值，在序列化过程中会被忽略（出现在非数组对象的属性值中时）或者被转换成null（出现在数组中时）；函数、undefined被单独转换时，会返回undefined；所以，当对象中含有函数时，就不能使用这个方法进行深拷贝。</p>
                    <p class="indent6">2、递归；可以处理函数</p>
                    <p class="title">数组拷贝方法：</p>
                    <p class="indent6">concat:只是对数组的第一层进行深拷贝、</p>
                    <p class="indent6">slice：只是对数组的第一层进行深拷贝、</p>
                    <p class="indent6">Object.assign：只是对对象的第一层进行深拷贝、</p>
                    <p class="indent6">展开运算符（...)：只是对数组和对象的第一层进行深拷贝。</p>
                </div>
                <div id="js8">
                    <h4>堆和栈</h4>
                    <p>栈(stack):由操作系统自动分配释放 ，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈；</p>
                    <p>堆(heap)：一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收，分配方式倒是类似于链表。</p>
                    <p class="title">区别</p>
                    <p class="indent6">一、缓存方式区别</p>
                    <p class="indent6">1.栈使用的是一级缓存， 他们通常都是被调用时处于存储空间中，调用完毕立即释放；</p>
                    <p class="indent6">2.堆是存放在二级缓存中，生命周期由虚拟机的垃圾回收算法来决定（并不是一旦成为孤儿对象就能被回收）。所以调用这些对象的速度要相对来得低一些。</p>
                    <p class="indent6">二、堆栈数据结构区别</p>
                    <p class="indent6">堆（数据结构）：堆可以被看成是一棵树，如：堆排序；</p>
                    <p class="indent6">栈（数据结构）：一种先进后出的数据结构。</p>
                    <p>
                        <img src="./堆栈.jpeg" alt="" srcset="">
                    </p>
                    <p class="title">堆栈溢出</p>
                    <p class="indent6">如果想要堆溢出，比较简单，可以循环创建对象或大的对象； 如果想要栈溢出，可以递归调用方法，这样随着栈深度的增加，JVM
                        （虚拟机）维持着一条长长的方法调用轨迹，直到内存不够分配，产生栈溢出。</p>
                </div>
                <div id="js9">
                    <h4>防抖和节流</h4>
                    <p>函数节流和函数防抖都是在限制被触发函数的执行次数，以解决函数触发频率过高导致浏览器响应速度跟不上出现的卡顿、延迟现象，这也就是前端的性能优化。</p>
                    <p>防抖：就是指触发事件后在n秒内函数只能执行一次，如果在n秒内又触发了事件，则会重新计算函数执行时间。</p>
                    <p>节流：就是指连续触发事件但是在一段时间中只执行一次函数。</p>
                </div>
                <div id="js10">
                    <h4>事件循环-EventLoop</h4>
                    <p class="title">概念</p>
                    <p class="indent6">任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程，在执行完毕后会退出主线程。</p>
                    <p class="indent6">因为JavaScript就是单线程，也就是说，同一个时间只能做一件事。单线程就意味着，所有任务需要排队，前一个任务结束，才会执行后一个任务。如果前一个任务耗时很长，后一个任务就不得不一直等着。</p>
                    <p class="title">同步(synchronous)和异步(asynchronous)</p>
                    <p class="indent6">同步任务：会立即执行的任务(除了异步任务都属于同步任务)</p>
                    <p class="indent6">异步任务：不会立即执行的任务(异步任务又分为宏任务与微任务)</p>
                    <p class="title">宏任务和微任务</p>
                    <p class="indent6">宏任务：由宿主对象发起的任务（setTimeout）</p>
                    <p>宏任务包括 script， setTimeout，setInterval，setImmediate(Node.js)，I/O，postMessage， MessageChannel，UI rendering</p>
                    <p class="indent6">微任务：由js引擎发起的任务（promise）</p>
                    <p>微任务包括 process.nextTick(Node.js)，promise.then()，promise.catch()，MutationObserver</p>
                    <p class="indent6" style="color:yellowgreen">任务的执行顺序：同步任务 -> 微任务 -> 宏任务。</p>
                    <p class="title">EventLoop 的执行机制如下：</p>
                    <p class="indent6">（1）所有同步任务都在主线程上执行，形成一个执行栈（execution context stack）。</p>
                    <p class="indent6">（2）主线程之外，还存在一个"任务队列"（task queue）。只要异步任务有了运行结果，就在"任务队列"之中放置一个事件。</p>
                    <p class="indent6">（3）一旦"执行栈"中的所有同步任务执行完毕，系统就会读取"任务队列"，看看里面有哪些事件。那些对应的异步任务，于是结束等待状态，进入执行栈，开始执行。</p>
                    <p class="indent6">(4）主线程不断重复上面的第三步。</p>
                    <p>下面就是这个循环的步骤：</p>
                    <p class="indent6">1.把同步任务队列 或者 微任务队列 或者 宏任务队列中的任务放入主线程。</p>
                    <p class="indent6">2.同步任务 或者 微任务 或者 宏任务在执行完毕后会全部退出主线程。</p>
                    
                    <p>在实际场景下大概是这么一个顺序：</p>
                    <p class="indent6">1.把同步任务相继加入同步任务队列。</p>
                    <p class="indent6">2.把同步任务队列的任务相继加入主线程。</p>
                    <p class="indent6">3.待主线程的任务相继执行完毕后，把主线程队列清空。</p>
                    <p class="indent6">4.把微任务相继加入微任务队列。</p>
                    <p class="indent6">5.把微任务队列的任务相继加入主线程。</p>
                    <p class="indent6">6.待主线程的任务相继执行完毕后，把主线程队列清空。</p>
                    <p class="indent6">7.把宏任务相继加入宏任务队列。无time的先加入，像网络请求。有time的后加入，像setTimeout(()=>{},time)，在他们中time短的先加入。</p>
                    <p class="indent6">8.把宏任务队列的任务相继加入主线程。</p>
                    <p class="indent6">9.待主线程的任务相继执行完毕后，把主线程队列清空。</p>
                </div>
                <div id="js11">
                    <h4>重绘和回流</h4>
                    <p class="title">重绘</p>
                    <p class="indent6">重绘就是重新绘画，重绘是一个元素外观的改变所触发的浏览器行为，当给一个元素更换颜色、更换背景，虽然不会影响页面布局，但是颜色或背景变了，浏览器就会根据元素的新属性重新绘制，这就是重绘；重绘不会引起 dom 结构和页面布局的变化，只是样式的变化，有重绘不一定有回流</p>
                    <p class="title">回流</p>
                    <p class="indent6">当增加或删除 dom 节点，或者给元素修改宽高时，会改变页面布局，那么就会重新构造 dom 树然后再次进行渲染，这就是回流；回流则是会引起 dom 结构和页面布局的变化，有回流就一定有重绘；不管怎么说都是会影响性能</p>
                    <p class="indent6">PS:重绘不会引起 dom 结构和页面布局的变化，只是样式的变化，有重绘不一定有回流
                        回流则是会引起 dom 结构和页面布局的变化，有回流就一定有重绘
                        不管怎么说都是会影响性能</p>
                    <p class="title">怎么进行优化或减少？</p>
                    <p class="indent6">1、多个属性尽量使用简写，例如：boder 可以代替 boder-width、boder-color、boder-style
                        <br/>
                        2、创建多个 dom 节点时，使用 documentfragment 创建<br/>
                        3、避免使用 table 布局<br/>
                        4、避免设置多层内联样式，避免节点层级过多<br/>
                        5、避免使用 css 表达式<br/>
                        6、将频繁重绘或回流的节点设置为图层，图层能够阻止该节点的渲染行为影响到别的节点（例：will-change \ video \ iframe等标签），浏览器会自动将该节点变为图层<br/>
                        7、脱离文档流之后进行的任何操作，都不会造成回流了，复杂操作的地方，不妨使用 position：absolute / fixed定位</p>
                </div>
                <div id="js12">
                    <h4>闭包</h4>
                    <p class="title">概念：</p>
                    <p class="indent6">函数执行，形成一个独立作用域，保护里边的私有变量不受外界的干扰，除了保护私有变量外，还可以存储一些内容，这样的模式叫做闭包。</p>
                    <p class="title">作用：</p>
                    <p class="indent6">通过一系方法,将函数内部的变量(局部变量)转化为全局变量</p>
                    <p class="title">优缺点：</p>
                    <p class="indent6">
                        优点：1.缓存、2.面向对象中的对象、 3.实现封装，防止变量跑到外层作用域中，发生命名冲突、4.匿名自执行函数，匿名自执行函数可以减小内存消耗
                    </p>
                    <p class="indent6">
                        缺点：1.内存消耗：通常来说，函数的活动对象会随着执行期上下文一起销毁，但是，由于闭包引用另外一个函数的活动对象，因此这个活动对象无法被销毁，这意味着，闭包比一般的函数需要更多的内存消耗。闭包会在IE下导致内存泄露
                        2.性能问题：使用闭包时，会涉及到跨作用域访问，每次访问都会导致性能损失。
                    </p>
                </div>
                <div id="js13">
                    <h4>作用域和作用域链</h4>
                    <p class="title">作用域:<br/>全局作用域、函数作用域(指声明在函数内部的变量，和全局作用域相反，局部作用域一般只在固定的代码片段内可访问到)、块级作用域(块级作用域可通过let和const声明，所声明的变量在指定块的作用域外无法被访问)</p>
                    <p class="indent6">当前的执行上下文，值和表达式在其中“可见”或可被访问。作用域最大的用处就是隔离变量，变量不会外泄、暴露出去。不同作用域下的同名变量不会有冲突。</p>
                    <p class="title">作用域链：访问变量时，自己的作用域中没有，一层一层向上寻找，直到找到全局作用域还是没找到，就宣布放弃，这种一层一层的关系，就是作用域链。作用域链有一个非常重要的特性，那就是作用域中的值是在函数创建的时候，就已经被存储了，是静态的。</p>
                </div>
            </div>
            <h3>浏览器</h3>
            <div>
                <div id="llq1">
                    <h4>浏览器输入URL发生什么</h4>
                    <p>1、浏览器会先查看缓存里是否有这个地址的页面，有就显示，没有就下一步</p>
                    <p>2、dns域名解析，浏览器向DNS服务器发起请求，解析该URL中的域名对应的IP地址。DNS服务器是基于UDP的，因此会用到UDP协议</p>
                    <p>3、建立TCP连接</p>
                    <p>4、发起http请求：浏览器发起读取文件的HTTP请求，，该请求报文作为TCP三次握手的第三次数据发送给服务器</p>
                    <p class="indent6">第一次握手：客户端往服务器发送syn报文，等待服务器确认</p>
                        <p class="indent6">第二次握手：服务器接收到客户端的请求，向客户端发送一个确认消息，服务端再向客户端发送一个syn包建立连接的请求</p>
                        <p class="indent6">第三次握手：客户端收到服务端的回复，建立连接</p>
                        <p class="indent6">为什么是三次握手：主要是为了建立可靠的通信通道，保证客户端与服务端同时具备发送、接收数据的能力</p>
                    <p>5、服务器响应请求并返回结果：服务器对浏览器请求做出响应，并把对应的html文件发送给浏览器</p>
                    <p>6、关闭TCP连接：四次挥手释放TCP连接</p>
                        <p class="indent6">第一次挥手: 客户端向服务器端发送断开 TCP 连接请求的</p>
                        <p class="indent6">第二次挥手：当服务器端收到客户端发来的断开 TCP 连接的请求后，回复发送 ACK 报文，表示已经收到断开请求</p>
                        <p class="indent6">第三次挥手: 服务器端在回复完客户端的 TCP 断开请求后，不会马上进行 TCP 连接的断开。服务器端会先确认断开前，所有传输到客户端的数据是否已经传输完毕。确认数据传输完毕后才进行断开，向客户端发送 [FIN，ACK] 报文</p>
                        <p class="indent6">第四次挥手：客户端收到服务器发来的 TCP 断开连接数据包后将进行回复，表示收到断开 TCP 连接数据包</p>
                    <p>7、浏览器渲染</p>
                    <p class="indent6">浏览器渲染过程：1、渲染引擎首先通过网络获得所请求文档的内容</p>
                    <p class="indent6">浏览器渲染过程：2、解析HTML文件，构建 DOM Tree</p>
                    <p class="indent6">浏览器渲染过程：3、解析CSS，构建 CSSOM Tree(CSS规则树)</p>
                    <p class="indent6">浏览器渲染过程：4、将 DOM Tree 和 CSSOM Tree合并，构建Render tree(渲染树)</p>
                    <p class="indent6">浏览器渲染过程：5、reflow(重排)：根据Render tree进行节点信息计算（Layout）</p>
                    <p class="indent6">浏览器渲染过程：6、repaint(重绘)：根据计算好的信息绘制整个页面（Painting）</p>
                    <p>扩展：</p>
                    <p class="indent6">何时会引起重排？</p>
                    <p class="indent6">（1）页面首次渲染。</p>
                    <p class="indent6">（2）元素尺寸改变——边距、填充、边框、宽度和高度</p>
                    <p class="indent6">（3）元素位置改变——display、float、position、overflow等等；</p>
                    <p class="indent6">（4）内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变；</p>
                    <p class="indent6">（5）操作DOM时；</p>
                    <p class="indent6">如何减少重排，优化性能</p>
                    <p class="indent6">（1）不要一个个的修改 DOM 的样式，应通过 class 来修改。</p>
                    <p class="indent6">（2）实现元素的动画，它的position属性，最好是设为absoulte或fixed，这样不会影响其他元素的布局</p>
                    <p class="indent6">（3）使用DocumentFragment将需要多次修改的DOM元素缓存，最后一次性append到真实DOM中渲染</p>
                    <p class="indent6">（4）可以将需要多次修改的DOM元素设置display:none，操作完再显示。（因为隐藏元素不在render树内，因此修改隐藏元素不会触发回流重绘）</p>
                    <p class="indent6">重排一定会引起重绘，而重绘不一定会引起重排。</p>

                </div>
                <div id="llq2">
                    <h4>跨域</h4>
                    <p>跨域的产生来源于现代浏览器所通用的‘同源策略’，协议名，域名，端口号都一样才不会跨域。</p>
                    <p class="title">解决方案</p>
                    <p>1、 通过jsonp跨域</p>
                    <p>2、 document.domain + iframe跨域：仅限主域相同，子域不同的跨域应用场景</p>
                    <p>3、 location.hash + iframe</p>
                    <p>4、 window.name + iframe跨域</p>
                    <p>5、 postMessage跨域</p>
                    <p>6、 跨域资源共享（CORS）：只服务端设置Access-Control-Allow-Origin即可，前端无须设置</p>
                    <p>7、 nginx代理跨域</p>
                    <p>8、 nodejs中间件代理跨域</p>
                    <p>9、 WebSocket协议跨域：WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信，同时允许跨域通讯，是    server push技术的一种很好的实现。</p>
                </div>
                <div id="llq3">
                    <h4>垃圾回收</h4>
                    <p>垃圾回收机制最常见的有两种：1.标记清除算法。2：引用计数算法。标记清除算法就是执行前将所有的变量打上标记，执行完成后未被打上标记的变量就会当做垃圾回收。浏览器会隔一段时间进行一次标记清除标记清除算法的缺点：被释放的掉的内存空间是不连续被，之前一直占据内存的变量隔开，导致内存空间不连续，会出现分配不一的问题。引用计数算法：就是对对象的引用次数进行增加或者减少，如果引用次数变为0，那么该对象分配的内存空间立即就会被清除掉。缺点就是会出现相互引用的情况。会导致对象一直被引用无法被清除</p>
                </div>
                <div id="llq4">
                    <h4>内存溢出:常发性、偶发性、一次性、隐式性</h4>
                    <p>1、内存中加载的数据量过于庞大，如一次从数据库取出过多数据。
                        解决方法：检查对数据库查询中，是否有一次获得全部数据的查询；对于数据库查询尽量采用分页的方式查询。</p>
                    <p>2、集合类中有对对象的引用，使用完后未清空，使得JVM不能回收。
                        解决方法：检查List、MAP等集合对象是否有使用完后，未清除的问题。List、MAP等集合对象会始终存有对对象的
                        引用，使得这些对象不能被GC回收。</p>
                    <p>3、代码中存在死循环或循环产生过多重复的对象实体。
                        解决方法：检查代码中是否有死循环或递归调用；检查是否有大循环重复产生新对象实体。</p>
                    <p>4.使用的第三方软件中的BUG。
                        解决方法：使用内存查看工具动态查看内存使用情况。</p>
                    <p>5.启动参数内存值设定的过小；
                        解决方法：修改JVM启动参数(-Xms，-Xmx)，直接增加内存。</p>
                </div>
                <div id="llq5">
                    <h4>内存泄漏</h4>
                    <p>是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放，造成系统内存的浪费，导致程序运行速度减慢甚至系统崩溃等严重后果。</p>
                </div>
            </div>
            <h3>WebPack</h3>
            <div>
                <div id="web1">
                    <h4>配置 <a href="https://www.lmlphp.com/user/742/article/item/14346/" target="_blank">连接</a></h4>
                    <p class="title">目录结构</p>
                    <p class="title">基础 npm 包</p>
                    <p class="title">基本命令</p>
                    <p class="indent6">
                        "scripts": {
                            "dev": "node dev.js",
                            "build": "node build.js",
                            "analyze": "node analyze.js",
                          }
                    </p>
                    <p class="title">dev.js 配置</p>
                    <p class="title">build.js 配置</p>
                    <p class="title">扩展配置</p>
                    <p class="indent6">比如添加插件、加载器等</p>
                </div>
                <div id="web2">
                    <h4>vite和webpack区别</h4>
                    <p>1、webpack是先打包再启动开发服务器，vite是直接启动开发服务器，然后按需编译依赖文件。</p>
                    <p>2、由于现代浏览器本身就支持ES Modules，会主动发起请求去获取所需文件。vite充分利用这点，将开发环境下的模块文件，就作为浏览器要执行的文件，而不是像webpack先打包，交给浏览器执行的文件是打包后的；</p>
                    <p>3、由于vite启动的时候不需要打包，也就无需分析模块依赖、编译，所以启动速度非常快。当浏览器请求需要的模块时，再对模块进行编译，这种按需动态编译的模式，极大缩短了编译时间，当项目越大，文件越多时，vite的开发时优势越明显；</p>
                    <p>4、当需要打包到生产环境时，vite使用传统的rollup进行打包，所以，vite的优势是体现在开发阶段，另外，由于vite使用的是ES Module，所以代码中不可以使用CommonJs；</p>
                </div>
            </div>
            <h3>数据库</h3>
            <div>
                <div id="navicate1">
                    <p class="title">1.主扩展模式</p>
                    <p class="indent6">主扩展模式通常用来将几个相似的对象的共有属性抽取出来，形成一个”公共属性表“，且“公共属性表”与“专有属性表”是“一对一”的关系。---对象的个数不多；各个对象之间的属性有一定差别；各个对象的属性在数据库设计阶段能够完全确定；各个扩展对象有独立的、相对比较复杂的业务处理需求，此时用“主扩展模式”。将各个对象的共有属性抽取出来设计为“主表”，将各个对象的剩余属性分别设计为相应的“扩展表”，“主表”与各个“扩展表”分别建立一对一的关系。</p>
                    <p class="title">2.主从模式</p>
                    <p class="indent6">主从模式，是数据库设计模式中最常见，也是大家日常设计工作中用的最多的一种模式，他描述了两个表之间的主从关系，是典型的一对多关系。---对象的个数较多且不固定；各个对象之间的属性几乎没有差异；对象的属性在数据库设计阶段能够完全确定；各个对象没有独立的业务处理需求，此时用“主从模式”。将各个对象设计为“从表”的记录，与“主表”对象建立一对多的关系。</p>
                    <p class="title">3.名值模式</p>
                    <p class="indent6">名值模式，通常用来描述在系统设计阶段不能完全确定属性的对象，这些对象的属性在系统运行时会有很大的变更，或者是对个对象之间的属性存在很大的差异。---对象的个数极多；各个对象之间的属性有较大差异；对象属性在数据库设计阶段不能确定，或者在系统运行时有较大变更；各个对象没有相互独立的业务处理需求，此时用“名值模式”。</p>
                    <p class="title">4.多对多模式</p>
                    <p class="indent6">多对多模式，也是比较常见的一种数据库设计模式，他所描述的两个对象部分主次，地位对等，互为一对多关系。多对多模式需要在两个表之间建立一个关联表，这个关联表是多对多关系的核心。两个对象之间互为一对多关系，则使用“多对多模式”。</p>
                </div>
            </div>
        </div>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                menus: [
                    {
                        name:'CSS',
                        children:[
                            {
                                id:'css1',
                                name:'样式穿透'
                            },
                            {
                                id:'css2',
                                name:'BFC'
                            },
                            {
                                id:'css3',
                                name:'flex布局'
                            }
                        ]
                    },
                    {
                        name:'VUE',
                        children:[
                            {
                                id:'vue1',
                                name:'指令'
                            },
                            {
                                id:'vue2',
                                name:'生命周期'
                            },
                            {
                                id:'vue3',
                                name:'VUEX'
                            },
                            {
                                id:'vue4',
                                name:'路由相关'
                            },
                            {
                                id:'vue5',
                                name:'双向绑定原理'
                            },
                            {
                                id:'vue6',
                                name:'组件传值'
                            },
                            {
                                id:'vue7',
                                name:'props和data优先级'
                            },
                            {
                                id:'vue8',
                                name:'组件中 data 为什么是一个函数？'
                            },
                            {
                                id:'vue9',
                                name:'key的作用'
                            },
                            {
                                id:'vue10',
                                name:'diff算法'
                            },
                            {
                                id:'vue11',
                                name:'计算属性与监听属性'
                            },
                            {
                                id:'vue12',
                                name:'组件的创建过程'
                            },
                            {
                                id:'vue13',
                                name:'nextTick'
                            },
                            {
                                id:'vue14',
                                name:'Vue数据更新，页面却没有更新'
                            },
                            {
                                id:'vue15',
                                name:'vue2和vue3响应式原理'
                            }
                        ]
                    },
                    {
                        name:'ES6',
                        children:[
                            {
                                id:'es1',
                                name:'普通函数和箭头函数区别'
                            },
                            {
                                id:'es2',
                                name:'数组相关'
                            },
                            {
                                id:'es3',
                                name:'在Axios中取消请求最核心的方法是CanelToken；'
                            },
                            {
                                id:'es4',
                                name:'new发生了什么'
                            },
                            {
                                id:'es5',
                                name:'set和map的区别'
                            },
                            {
                                id:'es6',
                                name:'Promise'
                            },
                            {
                                id:'es7',
                                name:'ES6新特性'
                            }
                        ]
                    },
                    {
                        name:'JAVASCRIPT',
                        children:[
                            {
                                id:'js1',
                                name:'MVVM'
                            },
                            {
                                id:'js2',
                                name:'数字精度丢失'
                            },
                            {
                                id:'js3',
                                name:'柯里化'
                            },
                            {
                                id:'js4',
                                name:'复杂类型和基本类型区别'
                            },
                            {
                                id:'js5',
                                name:'原型、原型链'
                            },
                            {
                                id:'js6',
                                name:'js中的显式原型和隐式原型'
                            },
                            {
                                id:'js7',
                                name:'深拷贝和浅拷贝'
                            },
                            {
                                id:'js8',
                                name:'堆和栈'
                            },
                            {
                                id:'js9',
                                name:'防抖和节流'
                            },
                            {
                                id:'js10',
                                name:'事件循环-EventLoop'
                            },
                            {
                                id:'js11',
                                name:'重绘和回流'
                            },
                            {
                                id:'js12',
                                name:'闭包'
                            },
                            {
                                id:'js13',
                                name:'作用域和作用域链'
                            }
                        ]
                    },
                    {
                        name:'浏览器',
                        children:[
                            {
                                id:'llq1',
                                name:'浏览器输入URL发生什么'
                            },
                            {
                                id:'llq2',
                                name:'跨域'
                            },
                            {
                                id:'llq3',
                                name:'垃圾回收'
                            },
                            {
                                id:'llq4',
                                name:'内存溢出'
                            },
                            {
                                id:'llq5',
                                name:'内存泄漏'
                            }
                        ]
                    },
                    {
                        name:'Webpack',
                        children:[
                            {
                                id:'web1',
                                name:'基础配置'
                            }
                        ]
                    },
                    {
                        name:'数据库设计四种主要设计模式的一般使用原则',
                        children:[
                            {
                                id:'navicate1',
                                name:'数据库设计四种主要设计模式的一般使用原则'
                            }
                        ]
                    }
                ],
            },
        })
    </script>
</body>
</html>